package com.edfu.core.identitymgt.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.edfu.core.commons.exceptions.InvalidArgumentException;
import com.edfu.core.coursemgt.util.CourseMgtConstants;
import com.edfu.core.identitymgt.persistence.api.IIdentityMgtPersistence;
import com.edfu.core.identitymgt.persistence.entity.PermissionEntity;
import com.edfu.core.identitymgt.persistence.entity.RoleEntity;
import com.edfu.core.identitymgt.persistence.entity.UserEnrollmentEntity;
import com.edfu.core.identitymgt.persistence.entity.UserEntity;
import com.edfu.core.identitymgt.service.api.IIdentityMgtService;
import com.edfu.core.identitymgt.util.IdentityMgtModelConverter;
import com.edfu.core.identitymgt.vo.Permission;
import com.edfu.core.identitymgt.vo.Role;
import com.edfu.core.identitymgt.vo.RolePermission;
import com.edfu.core.identitymgt.vo.User;
import com.edfu.core.identitymgt.vo.UserEnrollment;
import com.edfu.core.institutemgt.persistence.api.IInstituteMgtPersistence;
import com.edfu.core.institutemgt.persistence.entity.BranchMemberEntity;
import com.edfu.core.institutemgt.persistence.entity.SectionMemberEntity;
import com.edfu.core.util.Response;

public class IdentityMgtServiceImpl implements IIdentityMgtService, UserDetailsService {
	
	@Autowired
	private IIdentityMgtPersistence identityMgtPersistence;
	
	@Autowired
	private IInstituteMgtPersistence instituteMgtPersistence;
	
	private PasswordEncoder passwordEncoder = new ShaPasswordEncoder();
	
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public void addRole(Role role) {
		RoleEntity re = IdentityMgtModelConverter.getRoleEntity(role);
		identityMgtPersistence.persist(re);
	}

	@Override
	public User getUser(String userId) {
		return null;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public void addUser(User user) {
		_validateUser(user);
		user.setPassword(passwordEncoder.encodePassword(user.getPassword(), user.getUserId()));
		identityMgtPersistence.persist(IdentityMgtModelConverter.getUserEntity(user));
		//TODO
		// send email notification
		/*EDUMailMessage mailMessage = new EDUMailMessage();
		mailMessage.setBcc(bcc);
		mailMessage.setBodyModel(bodyModel);
		mailMessage.setBodyTemplateId(bodyTemplateId);
		mailMessage.setCc(cc);
		mailMessage.setFooterTemplateId(footerTemplateId);
		mailMessage.setFrom("admin@edu.com");
		mailMessage.setHeaderTemplateId(headerTemplateId);
		mailMessage.setImages(images);
		mailMessage.setIncludeFooter(includeFooter);
		mailMessage.setIncludeHeader(includeHeader);
		mailMessage.setLocaleName(localeName);
		mailMessage.setReplyTo(replyTo);
		mailMessage.setSentDate(sentDate);
		mailMessage.setSubject(subject);
		mailMessage.setSubjectModel(subjectModel);
		mailMessage.setSubjectTemplateId(subjectTemplateId);
		mailMessage.setText(text);
		mailMessage.setTo(to);*/	
	}

	@Override
	public boolean userExists(String userId) {
		return (identityMgtPersistence.getUser(userId) != null) ? true : false;
	}
	
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public void enrollUser(UserEnrollment enrollment) {
		User u = enrollment.getUser();
		if (!userExists(u.getUserId())) {
			addUser(u);
		}
		UserEntity ue = identityMgtPersistence.getUser(u.getUserId());
		UserEnrollmentEntity uee = IdentityMgtModelConverter.getUserEnrollmentEntity(enrollment);
		uee.setUser(ue);
		uee.setRole(identityMgtPersistence.getRole(enrollment.getRoleName()));
		identityMgtPersistence.persist(uee);
	}
	
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public Response addMember(UserEnrollment member, String enrollmentCtxType, String ctxId) {
		this.enrollUser(member);
		if (enrollmentCtxType.equalsIgnoreCase(CourseMgtConstants.EnrollmentContext.SECTION.toString())) {
			SectionMemberEntity memberEntity = new SectionMemberEntity();
			memberEntity.setAddedOn(Calendar.getInstance().getTime());
			memberEntity.setSection(instituteMgtPersistence.getSection(ctxId));
			memberEntity.setUserEnrollment(identityMgtPersistence.getUserEnrollment(member.getUser().getUserId(), member.getRoleName()));
			instituteMgtPersistence.persist(memberEntity);
		}else if(enrollmentCtxType.equalsIgnoreCase(CourseMgtConstants.EnrollmentContext.BRANCH.toString())) {
			BranchMemberEntity memberEntity = new BranchMemberEntity();
			memberEntity.setAddedOn(Calendar.getInstance().getTime());
			memberEntity.setBranch(instituteMgtPersistence.getBranch(ctxId));
			memberEntity.setRole(identityMgtPersistence.getRole(member.getRoleName()));
			memberEntity.setMember(identityMgtPersistence.getUser(member.getUser().getUserId()));
			instituteMgtPersistence.persist(memberEntity);
		}
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}

	@Override
	public boolean isUserEnrolled(String userId, String roleName) {
		return (identityMgtPersistence.getUserEnrollment(userId, roleName) != null) ? true : false;
	}

	@Override
	public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException, DataAccessException {
		UserEntity ue = identityMgtPersistence.getUser(userId);
		
		return new org.springframework.security.core.userdetails.User(ue.getUserId(), ue.getPassword(), ue.isEnabled(), ue.isAccountNonExpired(), ue.isCredentialsNonExpired(), ue.isAccountNonLocked(), getAuthorities(userId));		
	}

	public Collection<GrantedAuthority> getAuthorities(String userId) {
		List<UserEnrollmentEntity> ueeList = identityMgtPersistence.getRoles(userId);
		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		
		// Get Permissions and for each role and put them as Granted Authorities
		List<PermissionEntity> permissions = getPermissions(ueeList);
		for (PermissionEntity pe : permissions) {
			authorities.add(new GrantedAuthorityImpl(pe.getName()));
		}
		
		return authorities;
	}
	
	private List<PermissionEntity> getPermissions(List<UserEnrollmentEntity> ueeList) {
		List<PermissionEntity> permList = new ArrayList<PermissionEntity>();
		for (UserEnrollmentEntity uee : ueeList) {
			permList.addAll(identityMgtPersistence.getPermissions(uee.getRole()));
		}
		
		return permList;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public void addPermission(Permission permission) {
		PermissionEntity pe = IdentityMgtModelConverter.getPermissionEntity(permission);
		identityMgtPersistence.persist(pe);
	}
	
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public void assignPermission(RolePermission rolePermission) {
		identityMgtPersistence.assignPermission(rolePermission);		
	}
	
	
	/**Validation Methods**/
	private void _validateUser(User user) {
		if(user.getUserId() == null || user.getUserId().trim().length() == 0) {
			throw new InvalidArgumentException("Invalid User ID");
		}
		if(user.getPassword()== null || user.getPassword().trim().length() == 0) {
			throw new InvalidArgumentException("Invalid Password");
		}
		if(user.getEmail() == null || user.getEmail().trim().length() == 0) {
			throw new InvalidArgumentException("Invalid Email ID");
		}
	}
		
}
